/*************************************************************************
  Crytek Source File.
  Copyright (C), Crytek Studios, 2001-2010.
 -------------------------------------------------------------------------
  $Id$
  $DateTime$

	Managing class for IKTargets
	This class keeps a list of active IKTargets for characters.
	Use this class' interfaces to start/stop IK for characters.
  
 -------------------------------------------------------------------------
  History:
  - Oct 5th, 2010  : Created by Michelle Martin
 *************************************************************************/

#ifndef __IKTARGET_SYSTEM_H__
#define __IKTARGET_SYSTEM_H__

#if _MSC_VER > 1000
# pragma once
#endif


#include <IIKTargetSystem.h>
#include "IKTargetBase.h"

class CIKTargetSystem;



struct SIKCallbackData
{
	EntityId					entityID;
	CIKTargetSystem*  ikTargetSystem;

	void Set(EntityId id, CIKTargetSystem* iksystem)
	{
		entityID = id;
		ikTargetSystem = iksystem;
	}
};

// ===================================================================
// ===================================================================

class CIKTargetSystem : public IIKTargetSystem
{
public:

	// Constructors
	// ===========================
	CIKTargetSystem();
	virtual ~CIKTargetSystem();

	// Functions
	// ===========================

	//! stops and removes all IK for all entities immediately (no further updates)
	virtual void StopAllIK();

	//! stops and removes all IK for this character immediately (no further updates)
	virtual void StopAllIKOnCharacter( EntityId entityID );

	//! Fades out the IK for this character on the specified limb. 
	//! The IK is not necessarily immediately turned off, but the target is blended out instead.
	//! If either the character or the limb cannot be found, the function will do nothing and return.
	virtual void StopIKonCharacterLimb( EntityId entityID, const char* ikLimbName, float fadeOutTime = 0.3f );
	virtual void StopIKonCharacterLimb( EntityId entityID, uint64 ikLimbHash, float fadeOutTime = 0.3f );

	//! Sets a new offset for the specified IK Target
	virtual void SetOffsetOnTarget( EntityId entityID, uint64 ikLimbHash, QuatT newOffset );

	//! Adds an IK Target for an entity as a target
	//! returns 0 if ok, error code otherwise, see ::EIK_ErrorCodes
	virtual int AddIKTarget_EntityPos(const SIKTargetParams& ikParams, EntityId targetEntity);

	//! Adds an IK Target for a helper dummy as a target (created and exported in a 3D software)
	//! returns 0 if ok, error code otherwise, see ::EIK_ErrorCodes
	virtual int AddIKTarget_AttachedHelperPos(const SIKTargetParams& ikParams, EntityId targetEntity, const char* helperName);

	//! Adds an IK Target for an attachment as a target
	//! returns 0 if ok, error code otherwise, see ::EIK_ErrorCodes
	virtual int AddIKTarget_Attachment(const SIKTargetParams& ikParams, EntityId targetEntity, const char* attachmentName);

	//! Adds an IK Target for a character's bone as a target
	//! returns 0 if ok, error code otherwise, see ::EIK_ErrorCodes
	virtual int AddIKTarget_Bone(const SIKTargetParams& ikParams, EntityId targetEntity, const char* boneName);
	virtual int AddIKTarget_Bone(const SIKTargetParams& ikParams, EntityId targetEntity, uint boneId);

	//! Returns true if an IK Target exists for this limb for this character.
	//! False in all other cases.
	//! Also returns true if IK Target exists but the state is fully blended out.
	virtual bool ExistsIKTargetForCharacterLimb( EntityId entityID, uint64 ikLimbHash );

	//! Returns true if the IK Target for this character and limb exists and is 100% blended in.
	//! If no IK Target was found or it is not blended in completely, function will return false.
	virtual bool IsIKTargetForCharacterLimbFullyBlendedIn( EntityId entityID, uint64 ikLimbHash );


	//! Updates the IKs for this character instance, DO NOT CALL DIRECTLY
	//! This is being called from a callback function in CryAnimation after the 
	//! character has been updated completely
	void UpdateIKforCharacter( EntityId entityID );

	//! Returns the IK Target for the character-limb combination, NULL if none exists
	CIKTarget_Base* GetIKTargetForCharacterLimb( EntityId entityID, uint64 ikLimbHash );

	//! Writes a human readable error message for EIK_ErrorCodes into the console/log file.
	//! Calling code that got a fail when trying to add an IKTarget can use this
	//! to output a notification to the console.
	static void WriteErrorMessage(int errorCode);

	//! Set the entity for which to output debug information for
	static void ChangeDebugEntity( EntityId entityID );

private:

	//! the IK handles strings are mapped to a uint64 value - as this is what the animation system uses internally
	//! this way the maps first key can be directly re-used as an id for the ik limb to use
	typedef std::map<uint64, CIKTarget_Base*>	TActiveCharacterIKs;
	typedef std::vector<TActiveCharacterIKs::iterator> TIKTargetIteratorVector;
	struct SSpineIKData
	{
		bool	canBeRemoved;
		bool	b1BlendingOut;
		bool	b2BlendingOut;
		uint64	limbHandle1;
		uint64	limbHandle2;
		Vec3	target1;
		Vec3	target2;
		float	b1, b2;
		QuatT	spineTargetWorld;

		SSpineIKData()
		{
			canBeRemoved = false;
			b1 = 0.0f;
			b2 = 0.0f;
			b1BlendingOut = false;
			b2BlendingOut = false;
			limbHandle1 = 0;
			limbHandle2 = 0;
		}
	};
	typedef std::map<uint64, SSpineIKData> TSpineIKData;
	struct SCharacterIKData
	{
		//! List IK handles and IK Targets
		TActiveCharacterIKs		m_activeCharIKs;

		//! IK Callback data for this character
		SIKCallbackData			m_callbackData;

		//! Data for interpolating and blending the spine
		TSpineIKData			m_spineIKData;

		//! for storing the original callback - this will be called
		//! after this update is done and be restored upon finishing all IKs
		int (*m_pOriginalPostProcessCallback)(ICharacterInstance*,void*);
		void* m_pOriginalPostProcessCallbackData;

	};


	//! function to check whether the ikParams are valid, and all interfaces exist
	//! this is used in all Add IKTarget functions - so it is extracted to avoid code duplication
	int SafetyCheck_IKParams(const SIKTargetParams &ikParams );

	//! Helper function to retrieve this character's IK Data
	//! If the character does not yet have an entry in the list, one will be created!
	SCharacterIKData* GetCharacterData(const SIKTargetParams &ikParams );

	//! Helper function to delete the currently active IK target and retrieve it's current world location
	//! This is used when a new IK is started and blended over from the current target's position
	QuatT DeleteCurrentTarget( SCharacterIKData* charIKData, const SIKTargetParams &ikParams );

	//! Sets the callback that comes from the animation system after processing all kinematics to this class.
	//! And it stores the original callback and data inside charIKData, so it can be called once this system has updated.
	//! The callback will only be set if it isn't already - multiple calls to this function will not break functionality.
	void RerouteAnimCallback( EntityId entityID, SCharacterIKData* charIKData );

	//! restores the original callback again
	void RestoreAnimCallback( ICharacterInstance * pCharacter, SCharacterIKData* charIKData );

	//! Helper function to calculate a good target position for the spine
	QuatT CalculateSpineTarget_Type02( EntityId entityID, uint64 spineHandle, SSpineIKData* spineIKData, const SIKTargetCharData* charIKTargetData );

	//! Helper function, returns 0.0f if within range
	//! combined squared distance otherwise
	float AreRootsWithinTargetSpheres(Vec3 R1, Vec3 T1, float maxDist1, Vec3 R2, Vec3 T2, float maxDist2, int rootCount);

	typedef std::map<EntityId, SCharacterIKData*> TActiveIKs;
	TActiveIKs						m_activeIKs;

	// Entity for which to output debug information for
	static EntityId				m_debugEntity;
};


#endif // __IKTARGET_SYSTEM_H__




